package org.example.parser;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;

/**
 * flow标签解析
 */
public class FlowParser implements BeanDefinitionParser {


    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        String className = element.getAttribute("class");
        if (StringUtils.isEmpty(className)) {
            throw new IllegalStateException("Sequence class is null");
        }

        Class classObj;
        try {
            classObj = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException("class NotFound:" + className);
        }

        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(classObj);
        beanDefinition.setLazyInit(false);

        String id = element.getAttribute("id");
        if (StringUtils.isEmpty(id)) {
            throw new IllegalStateException("[sequence]This bean do not set spring bean id " + id);
        } else {
            if (parserContext.getRegistry().containsBeanDefinition(id)) {
                throw new IllegalStateException("[sequence]Duplicate spring bean id " + id);
            }
            parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
        }

        Method[] methods = classObj.getMethods();
        int len$ = methods.length;
        for (int i$ = 0; i$ < len$; ++i$) {
            Method setter = methods[i$];
            if (this.isProperty(setter)) {
                String name = setter.getName();
                String property = name.substring(3, 4).toLowerCase() + name.substring(4);
                int proType = this.getPropertyType(property);
                String value = element.getAttribute(property);
                RuntimeBeanReference reference;
                switch (proType) {
                    //引用
                    case 1:
                        if (!StringUtils.isEmpty(value)) {
                            BeanDefinition refBean = parserContext.getRegistry().getBeanDefinition(value);
                            if (!refBean.isSingleton()) {
                                throw new IllegalStateException("[sequence]The exported service ref " + value + " must be singleton! Please set the " + value + " bean scope to singleton, eg: <bean id=\"" + value + "\" scope=\"singleton\" ...>");
                            }
                            reference = new RuntimeBeanReference(value);
                        } else {
                            reference = null;
                        }
                        beanDefinition.getPropertyValues().addPropertyValue(property, reference);
                        break;
                    // 值
                    case 2:
                        if (!StringUtils.isEmpty(value)) {
                            beanDefinition.getPropertyValues().addPropertyValue(property, value);
                        }
                        break;
                    //子集 list
                    case 3:
                        NodeList nodeList = element.getElementsByTagName("flow:" + property);

                        if (nodeList != null && nodeList.getLength() >= 1) {
                            NodeList childNodes = nodeList.item(0).getChildNodes();
                            List list = new ManagedList();
                            for (int i = 0, j = childNodes.getLength(); i < j; i++) {
                                Node node = childNodes.item(i);
                                NamedNodeMap namedNodeMap = node.getAttributes();
                                if (null != namedNodeMap && namedNodeMap.getLength() >= 1) {
                                    list.add(new RuntimeBeanReference(namedNodeMap.item(0).getNodeValue()));
                                }
                            }
                            beanDefinition.getPropertyValues().addPropertyValue(property, list);
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        return beanDefinition;
    }


    private int getPropertyType(String propertyName) {
        byte type = -1;
        if ("components".equals(propertyName)) {
            type = 3;
        } else if ("id".equals(propertyName)) {
            type = 2;
        }
        return type;
    }

    private boolean isProperty(Method method) {
        String methodName = method.getName();
        boolean flag = methodName.length() > 3
                && methodName.startsWith("set")
                && Modifier.isPublic(method.getModifiers())
                && method.getParameterTypes().length == 1;
        return flag;
    }
}
